/**
 * Copyright (C) 2009 aileron.cc
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package cc.aileron.template.parser.method;

import static java.util.regex.Pattern.compile;
import static java.util.regex.Pattern.quote;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cc.aileron.accessor.PojoAccessor;
import cc.aileron.commons.utils.SkipList;
import cc.aileron.template.context.TemplateContext;
import cc.aileron.template.parser.ParserMethod;
import cc.aileron.template.parser.ParserMethodProvider;

import com.google.inject.Singleton;

/**
 * @author Aileron
 */
@Singleton
public class RepMethodParserProvider implements ParserMethodProvider
{
    @Override
    public ParserMethod get(final String content,
            final String args)
    {
        final List<RepKeyAndValue> list = new SkipList<RepKeyAndValue>();
        for (final String value : getArgs(args))
        {
            logger.trace("args : ", value);
            final Matcher matcher = pattern.matcher(value);
            if (matcher.find())
            {
                final StringBuffer sb = new StringBuffer();
                final String key = matcher.group(1);
                matcher.appendReplacement(sb, "(.*?)")
                    .appendTail(sb);

                final String string = sb.toString();
                final RepKeyAndValue rep = new RepKeyAndValue();
                logger.trace("get#string : {}", string);
                rep.pattern = Pattern.compile(sb.toString());
                rep.key = key;
                list.add(rep);
            }
        }
        return new ParserMethod()
        {
            @Override
            public void call(final TemplateContext context)
            {
                String repContent = content;
                for (final RepKeyAndValue rep : list)
                {
                    repContent = rep(rep, repContent, context.getAccessor());
                }
                context.getNameSpace()
                    .append(repContent);
            }
        };
    }

    /**
     * @param rep
     * @param content
     * @param accessor
     * @return 置き換え後文字列
     */
    String rep(final RepKeyAndValue rep,
            final String content,
            final PojoAccessor<?> accessor)
    {
        final StringBuffer sb = new StringBuffer();
        final Matcher matcher = rep.pattern.matcher(content);
        while (matcher.find())
        {
            final CharSequence start = content.subSequence(matcher.start(),
                    matcher.start(1));
            final String value = accessor.get(rep.key)
                .toString();
            final CharSequence end = content.subSequence(matcher.end(1),
                    matcher.end());
            logger.trace("rep#value : {}", value);
            matcher.appendReplacement(sb, start + value + end);
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /**
     * @param args
     * @return args
     */
    private String[] getArgs(final String args)
    {
        logger.trace("getArgs#args   : {}", args);
        final String[] result = args.split(" ");
        logger.trace("getArgs#result : {}", result);
        if (result != null)
        {
            return result;
        }
        return new String[] { args };
    }

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final Pattern pattern = compile(quote("${") + "(.*?)" + quote("}"));
}

class RepKeyAndValue
{
    String key;
    Pattern pattern;
}